#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

#define BUFSIZE 10   //  缓冲区大小
#define CON_NUM 5    //  消费者数量
#define PRODUCTS 500 // 生产总数
const char *file_name = "./lab6buffer";
const char *full_name = "lab6full";
const char *empty_name = "lab6empty";
const char *mutex_name = "lab6mutex";
sem_t *empty, *full, *mutex;

void Producer()
{
    int fd, num = 0;
    while (num <= PRODUCTS)
    {
        sem_wait(empty);
        sem_wait(mutex);
        fd = open(file_name, O_WRONLY | O_APPEND); //追加写即可
        write(fd, &num, sizeof(int));
        close(fd);
        sem_post(mutex);
        sem_post(full);
        num++;
    }

    return;
}

void Consumer()
{
    int fd, ret;
    int buf[BUFSIZE];
    while (1)
    {
        sem_wait(full);
        sem_wait(mutex);
        //  取出缓冲区所有资源（方便做删除操作）
        fd = open(file_name, O_RDONLY);
        ret = read(fd, buf, BUFSIZE * sizeof(int)) / (sizeof(int)); // 计算一共取了几个数
        close(fd);
        if (ret)
        {
            printf("%d: %d\n", getpid(), buf[0]);
            fflush(stdout);
        }
        //  清空缓冲区文件，并写回文件其他数字，如果没有需要写回的，只做清空
        fd = open(file_name, O_WRONLY | O_TRUNC);
        if (ret > 1)
            write(fd, &buf[1], (ret - 1) * sizeof(int)); //其余ret-1个数字写回文件
        close(fd);
        sem_post(mutex);
        sem_post(empty);
    }
    return;
}

int main()
{
    int fd, i;
    pid_t pid;
    empty = sem_open(empty_name, O_CREAT, 0666, BUFSIZE); // 创建信号量empty，初始为最大值（剩余空间）
    full = sem_open(full_name, O_CREAT, 0666, 0);         // 创建信号量full，初始为0（已生产空间）
    mutex = sem_open(mutex_name, O_CREAT, 0666, 1);       // 创建互斥信号量，同时只有1个进程可以进行访问临界区

    // 创建文件
    fd = open(file_name, O_CREAT | O_RDWR | O_TRUNC, 0666);
    close(fd);
    // 创建生产者
    if (!(pid = fork()))
    {
        Producer();
        return 0;
    }
    // 创建消费者
    for (i = 0; i < CON_NUM; i++)
    {
        if (!fork())
        {
            Consumer();
            return 0;
        }
    }
    // 回收生产完毕的生产者进程
    waitpid(pid, NULL, 0);
    sleep(2);         //  稍微等一会？
    kill(0, SIGKILL); //杀死所有消费者进程
    //删除信号
    sem_unlink(full_name);
    sem_unlink(empty_name);
    sem_unlink(mutex_name);
    return 0;
}